#ifndef LOGGER_H
#define LOGGER_H

#define LOG_DEBUG(message) Logger::getInstance()->debug(message, __FILE__, __LINE__, Q_FUNC_INFO)
#define LOG_INFO(message) Logger::getInstance()->info(message, __FILE__, __LINE__, Q_FUNC_INFO)
#define LOG_WARNING(message) Logger::getInstance()->warning(message, __FILE__, __LINE__, Q_FUNC_INFO)
#define LOG_ERROR(message) Logger::getInstance()->error(message, __FILE__, __LINE__, Q_FUNC_INFO)
#define LOG_CRITICAL(message) Logger::getInstance()->critical(message, __FILE__, __LINE__, Q_FUNC_INFO)

#include <QObject>
#include <QFile>
#include <QTextStream>
#include <QMutex>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QCoreApplication>

enum class LogLevel {
    DEBUG = 0,
    INFO,
    WARNING,
    ERR,
    CRITICAL
};


class Logger : public QObject
{
    Q_OBJECT
public:
    /**
     * @brief 获取日志类单例实例
     * @return Logger实例指针
     */
    static Logger* getInstance();

    /**
     * @brief  初始化相关参数
     * @param  logDir 设置log文件夹名
     * @param  minLevel 输出类型索引
     * @param  consoleOutput 是否打印台输出
     */
    void init(const QString& logDir = "",
              LogLevel minLevel = LogLevel::DEBUG,
              bool consoleOutput = true);

    /**
     * @brief  拼接含日期、输出类型等日志输出数据
     * @param  level 输出类型索引
     * @param  message 输出的日志数据
     * @param  file 输出的日志文件路径
     * @param  line 输出命令所在文件行数
     * @param  function 输出命令所在文件函数名
     */
    void log(LogLevel level, const QString& message,
             const QString& file = "", int line = 0,
             const QString& function = "");

    /**
     * @brief  便捷方法的实现类似
     */
    void debug(const QString& message, const QString& file = "",
               int line = 0, const QString& function = "");
    void info(const QString& message, const QString& file = "",
              int line = 0, const QString& function = "");
    void warning(const QString& message, const QString& file = "",
                 int line = 0, const QString& function = "");
    void error(const QString& message, const QString& file = "",
               int line = 0, const QString& function = "");
    void critical(const QString& message, const QString& file = "",
                  int line = 0, const QString& function = "");

    /**
     * @brief  设置输出索引的最小值
     * @param  level 输出类型索引
     */
    void setMinLevel(LogLevel level);

    /**
     * @brief  设置内部参数
     */
    void setConsoleOutputEnabled(bool enabled);
    void setFileOutputEnabled(bool enabled);
    void setLogFileName(const QString& fileName);


public:
    /**
     * @brief  设置日志文件最大内存
     */
    void setMaxFileSize(qint64 size);

    /**
     * @brief  设置日志文件最大备份数
     */
    void setMaxBackupCount(int count);

signals:
    void logMessage(const QString& formattedMessage, const LogLevel& level);

private:
    /**
     * @brief  日志类初始化构造函数
     * @param  parent 父窗口
     */
    Logger(QObject* parent = nullptr);
    /**
     * @brief  日志类初始化析构函数
     */
    ~Logger();

    /**
     * @brief  获取对应输出类型
     * @param  level 输出类型索引
     * @return 应输出类型字符串
     */
    QString levelToString(LogLevel level) const;

    /**
     * @brief  封装输出数据
     * @param  level 输出类型索引
     * @param  message 输出的日志数据
     * @param  file 输出的日志文件路径
     * @param  line 输出日志所在的文件行数
     * @param  function 输出命令所在文件函数名
     * @return 返回输出的日志字符串
     */
    QString formatMessage(LogLevel level, const QString& message,
                         const QString& file, int line,
                         const QString& function) const;

    /**
     * @brief  日志文件备份
     */
    void rotateLog();

    static Logger* m_instance;
    QMutex m_mutex;
    LogLevel m_minLevel;
    bool m_consoleOutput;
    bool m_fileOutput;
    QFile m_logFile;
    QTextStream m_textStream;
    QString m_logFileName;

    qint64 m_maxFileSize;
    int m_maxBackupCount;
};

#endif // LOGGER_H
